﻿using System;
using System.Diagnostics;

using zijian666.Converts.Abstractions;
using zijian666.Converts.Core;

namespace zijian666.Converts.Filters;
internal class TraceFilter<T> : IConvertFilter<T>
{
    private readonly static DiagnosticListener _diagnostic = new MyDiagnosticListener();

    private static void TraceLog(DiagnosticSource? trace, string name, object? value)
    {
        if (trace != null && trace.IsEnabled(name))
        {
            trace.Write(name, value);
        }
    }

    public ConvertResult<T> OnConverting(IConvertContext context, IConvertor<T> convertor, object? value)
    {
        if (context["TRACE"] is not DiagnosticSource trace)
        {
            context["TRACE"] = trace = _diagnostic;
        }

        TraceLog(trace, "Converting", new { context, convertor, value });
        return ConvertResult<T>.FAIL;
    }

    public void OnConverted(IConvertContext context, IConvertor<T> convertor, object? value, ConvertResult<T> result)
    {
        var trace = context["TRACE"] as DiagnosticSource ?? _diagnostic;
        TraceLog(trace, "Converted", new { context, convertor, value, result });
    }

    public void OnException(IConvertContext context, IConvertor<T> convertor, object? value, Exception exception)
    {
        var trace = context["TRACE"] as DiagnosticSource ?? _diagnostic;
        TraceLog(trace, "Exception", new { context, convertor, value, exception });
    }

}

internal class MyDiagnosticListener()
    : DiagnosticListener($"{nameof(zijian666)}.{nameof(Converts)}")
{
    /// <summary>
    /// 为避免在记录时调用 <seealso cref="Converts.To{T}(object)"/> 引起的死循环
    /// </summary>
    [ThreadStatic]
    private static bool _tracing;

    public override bool IsEnabled(string name) => !_tracing && base.IsEnabled(name);

    public override bool IsEnabled(string name, object? arg1, object? arg2 = null) => !_tracing && base.IsEnabled(name, arg1, arg2);

    public override void Write(string name, object? value)
    {
        if (!_tracing)
        {
            _tracing = true;
            base.Write(name, value);
            _tracing = false;
        }
    }
}
